package com.seteam.collabcrossword;

import com.seteam.collabcrossword.messages.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CommunicationChannel implements Runnable {
    private final Logger logger = LoggerFactory.getLogger(CommunicationChannel.class);
    private Map<Long, CollabCrosswordWorker> threadMap;
    private ObservableQueue<Message> incomingQueue;
    private ObservableQueue<Message> outgoingQueue;
    private Socket socket;
    private ObjectOutputStream objectOutputStream;
    private OutputStream outputStream;
    private InputStream inputStream;
    private ObjectInputStream objectInputStream;

    public CommunicationChannel(final Socket socket) {
        this.socket = socket;
        initSocket();
        threadMap = new ConcurrentHashMap<Long, CollabCrosswordWorker>();
        initQueues();
    }

    @Override
    public void run() {
        while (true) {
            try {
                inputStream = socket.getInputStream();
                objectInputStream = new ObjectInputStream(inputStream);
                final Message message = (Message) objectInputStream.readObject();
                incomingQueue.add(message);
            } catch (final IOException e) {
                logger.error("Exception: {}", e.getMessage());
            } catch (final ClassNotFoundException e) {
                logger.error("Exception: {}", e.getMessage());
            }
        }
    }

    private void initQueues() {
        incomingQueue = new ObservableQueue<Message>();
        incomingQueue.registerObserver(new QueueObserver<Message>() {
            @Override
            public void onAdd(final Message element) {
                logger.info("An message was added to the incoming queue");
            }
        });

        outgoingQueue = new ObservableQueue<Message>();
        outgoingQueue.registerObserver(new QueueObserver<Message>() {
            @Override
            public void onAdd(final Message element) {
                logger.info("An message was added to the outgoing queue");
                writeMessageToSocket(outgoingQueue.remove());
            }
        });
    }

    private void initSocket() {
        try {
            outputStream = socket.getOutputStream();
            objectOutputStream = new ObjectOutputStream(outputStream);
        } catch (final IOException e) {
            logger.error("Exception: {}", e.getMessage());
        }
    }

    public void sendMessage(final CollabCrosswordWorker worker, final Message message) {
        addWorkerToMap(worker);
        outgoingQueue.add(message);
    }

    private void writeMessageToSocket(final Message message) {
        try {
            objectOutputStream.writeObject(message);
        } catch (final IOException e) {
            logger.error("Exception: {}", e.getMessage());
        }
    }

    private void addWorkerToMap(final CollabCrosswordWorker worker) {
        threadMap.put(worker.getID(), worker);
    }

    /**
     * Test method. Delete after testing
     */
    public void addToIncomingQueue(final Message message) {
        incomingQueue.add(message);
    }

    /**
     * Test method. Delete after testing
     */
    public void addToOutgoingQueue(final Message message) {
        outgoingQueue.add(message);
    }
}
